# ba_meta require api 7

"""
    Ragdoll-B-Gone by TheMikirog
    
    Removes ragdolls. 
    Thanos snaps those pesky feet-tripping body sacks out of existence.
    Literally that's it.
    
    Heavily commented for easy modding learning!
    
    No Rights Reserved

"""

from __future__ import annotations

from typing import TYPE_CHECKING

# Let's import everything we need and nothing more.
import ba
import bastd
import random
from bastd.actor.spaz import Spaz
from bastd.actor.spazfactory import SpazFactory

if TYPE_CHECKING:
    pass

# ba_meta export plugin


class RagdollBGone(ba.Plugin):

    # We use a decorator to add extra code to existing code, increasing mod compatibility.
    # Any gameplay altering mod should master the decorator!
    # Here I'm defining a new handlemessage function that'll be replaced.
    def new_handlemessage(func):
        # This function will return our wrapper function, which is going to take the original function's base arguments.
        # Yes, in Python functions are objects that can be passed as arguments. It's bonkers.
        # arg[0] is "self", args[1] is "msg" in our original handlemessage function.
        # We're working kind of blindly here, so it's good to have the original function
        # open in a second window for argument reference.
        def wrapper(*args, **kwargs):
            if isinstance(args[1], ba.DieMessage):  # Replace Spaz death behavior

                # Here we play the gamey death noise in Co-op.
                if not args[1].immediate:
                    if args[0].play_big_death_sound and not args[0]._dead:
                        ba.playsound(SpazFactory.get().single_player_death_sound)

                # If our Spaz dies by falling out of the map, we want to keep the ragdoll.
                # Ragdolls don't impact gameplay if Spaz dies this way, so it's fine if we leave the behavior as is.
                if args[1].how == ba.DeathType.FALL:
                    # The next two properties are all built-in, so their behavior can't be edited directly without touching the C++ layer.
                    # We can change their values though!
                    # "hurt" property is basically the health bar above the player and the blinking when low on health.
                    # 1.0 means empty health bar and the fastest blinking in the west.
                    args[0].node.hurt = 1.0
                    # Make our Spaz close their eyes permanently and then make their body disintegrate.
                    # Again, this behavior is built in. We can only trigger it by setting "dead" to True.
                    args[0].node.dead = True
                    # After the death animation ends (which is around 2 seconds) let's remove the Spaz our of existence.
                    ba.timer(2.0, args[0].node.delete)
                else:
                    # Here's our new behavior!
                    # The idea is to remove the Spaz node and make some sparks for extra flair.
                    # First we see if that node even exists, just in case.
                    if args[0].node:
                        # Make sure Spaz isn't dead, so we can perform the removal.
                        if not args[0]._dead:
                            # Run this next piece of code 4 times.
                            # "i" will start at 0 and becomes higher each iteration until it reaches 3.
                            for i in range(4):
                                # XYZ position of our sparks, we'll take the Spaz position as a base.
                                pos = (args[0].node.position[0],
                                       # Let's spread the sparks across the body, assuming Spaz is standing straight.
                                       # We're gonna change the Y axis position, which is height.
                                       args[0].node.position[1] + i * 0.2,
                                       args[0].node.position[2])
                               # This function allows us to spawn particles like sparks and bomb shrapnel.
                               # We're gonna use sparks here.
                                ba.emitfx(position=pos,  # Here we place our edited position.
                                          velocity=args[0].node.velocity,
                                          # Random amount of sparks between 2 and 5
                                          count=random.randrange(2, 5),
                                          scale=3.0,
                                          spread=0.2,
                                          chunk_type='spark')

                            # Make a Spaz death noise if we're not gibbed.
                            if not args[0].shattered:
                                # Get our Spaz's death noises, these change depending on character skins
                                death_sounds = args[0].node.death_sounds
                                # Pick a random death noise
                                sound = death_sounds[random.randrange(len(death_sounds))]
                                # Play the sound where our Spaz is
                                ba.playsound(sound, position=args[0].node.position)
                        # Delete our Spaz node immediately.
                        # Removing stuff is weird and prone to errors, so we're gonna delay it.
                        ba.timer(0.001, args[0].node.delete)

                # Let's mark our Spaz as dead, so he can't die again.
                # Notice how we're targeting the Spaz and not it's node.
                # "self.node" is a visual representation of the character while "self" is his game logic.
                args[0]._dead = True
                # Set his health to zero. This value is independent from the health bar above his head.
                args[0].hitpoints = 0
                return

            # Worry no longer! We're not gonna remove all the base game code!
            # Here's where we bring it all back.
            # If I wanted to add extra code at the end of the base game's behavior, I would just put that at the beginning of my function.
            func(*args, **kwargs)
        return wrapper

    # Finally we """travel through the game files""" to replace the function we want with our own version.
    # We transplant the old function's arguments into our version.
    bastd.actor.spaz.Spaz.handlemessage = new_handlemessage(bastd.actor.spaz.Spaz.handlemessage)
